<template>
  <div class="gaode-color-map-container">
    <!-- 颜色调整控制面板 -->
    <table class="controls">
      <tr>
        <td><label for="hue">色相</label></td>
        <td><input id="hue" type="range" min="-180" max="180" v-model.number="hue"/></td>
        <td><span>{{ hue }}</span>°</td>
      </tr>
      <tr>
        <td><label for="chroma">色度</label></td>
        <td><input id="chroma" type="range" min="0" max="100" v-model.number="chroma"/></td>
        <td><span>{{ chroma }}</span>%</td>
      </tr>
      <tr>
        <td><label for="lightness">亮度</label></td>
        <td><input id="lightness" type="range" min="0" max="100" v-model.number="lightness"/></td>
        <td><span>{{ lightness }}</span>%</td>
      </tr>
    </table>
    <!-- 地图容器 -->
    <div ref="mapContainer" class="map-view"></div>
  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted, watch, onBeforeUnmount } from 'vue';
import Map from 'ol/Map';
import View from 'ol/View';
import ImageLayer from 'ol/layer/Image';
import RasterSource from 'ol/source/Raster';
import XYZ from 'ol/source/XYZ';
import type { Map as OlMap } from 'ol';
import type { Operation } from 'ol/source/Raster';

// ========== 接口定义 ==========
interface RasterLibrary {
  rgb2xyz: (x: number) => number;
  xyz2rgb: (x: number) => number;
  xyz2lab: (t: number) => number;
  lab2xyz: (t: number) => number;
  twoPi: number;
  Xn: number;
  Yn: number;
  Zn: number;
}

// ========== 常量定义 ==========
const Xn = 0.95047;
const Yn = 1;
const Zn = 1.08883;
const t0 = 4 / 29;
const t1 = 6 / 29;
const t2 = 3 * t1 * t1;
const t3 = t1 * t1 * t1;
const twoPi = 2 * Math.PI;

// ========== 组件状态 ==========
// 地图容器引用
const mapContainer = ref<HTMLDivElement | null>(null);
// 色相调整值（-180到180度）
const hue = ref<number>(0);
// 色度调整值（0到100%）
const chroma = ref<number>(100);
// 亮度调整值（0到100%）
const lightness = ref<number>(100);
// 地图实例
let map: OlMap | null = null;

// ========== 地图源配置 ==========
// 配置高德地图瓦片源
const gaodeSource = new XYZ({
  crossOrigin: 'anonymous', // 允许跨域加载瓦片
  maxZoom: 18,
  minZoom: 3,
  // 使用高德地图瓦片服务，{1-4}表示负载均衡的服务器编号
  url: 'https://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7',
  attributions: '© 2024 高德地图 - GS(2024)1158号'
});

// 创建栅格图层源，用于实时处理地图瓦片颜色
const raster = new RasterSource({
  sources: [gaodeSource], // 使用高德地图作为基础图层
  // operation 函数在 Web Worker 中执行，用于处理每个像素的颜色
  operation: function(pixels: Array<ImageData | number[]>, data: { hue: number; chroma: number; lightness: number }): number[] {
    const pixel = pixels[0] as number[];

    // ===== 内部工具函数 =====
    // RGB到XYZ颜色空间的转换
    const rgb2xyz = (x: number): number => {
      return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
    };

    // XYZ到RGB颜色空间的转换
    const xyz2rgb = (x: number): number => {
      return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);
    };

    // XYZ到LAB颜色空间的转换
    const xyz2lab = (t: number): number => {
      const t1 = 6 / 29;
      const t3 = t1 * t1 * t1;
      const t2 = 3 * t1 * t1;
      const t0 = 4 / 29;
      return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;
    };

    // LAB到XYZ颜色空间的转换
    const lab2xyz = (t: number): number => {
      const t1 = 6 / 29;
      const t2 = 3 * t1 * t1;
      const t0 = 4 / 29;
      return t > t1 ? t * t * t : t2 * (t - t0);
    };

    // ===== 颜色空间转换常量 =====
    const Xn = 0.95047; // D65标准光源的X值
    const Yn = 1;       // D65标准光源的Y值
    const Zn = 1.08883; // D65标准光源的Z值
    const twoPi = 2 * Math.PI; // 用于角度计算

    // ===== 颜色转换过程 =====
    // 1. RGB到XYZ的转换
    const red = rgb2xyz(pixel[0]);
    const green = rgb2xyz(pixel[1]);
    const blue = rgb2xyz(pixel[2]);

    // 2. XYZ到LAB的转换
    const x = xyz2lab((0.4124564 * red + 0.3575761 * green + 0.1804375 * blue) / Xn);
    const y = xyz2lab((0.2126729 * red + 0.7151522 * green + 0.072175 * blue) / Yn);
    const z = xyz2lab((0.0193339 * red + 0.119192 * green + 0.9503041 * blue) / Zn);

    // 3. 计算LAB颜色空间的值
    const l = 116 * y - 16;  // 亮度
    const a = 500 * (x - y); // 红绿对立色
    const b = 200 * (y - z); // 黄蓝对立色

    // 4. 计算色度和色相
    const c = Math.sqrt(a * a + b * b); // 色度
    let h = Math.atan2(b, a);           // 色相（弧度）
    if (h < 0) h += twoPi;

    // 5. 应用用户的颜色调整
    h += (Math.PI * data.hue) / 180;    // 调整色相
    if (h < 0) h += twoPi;
    else if (h > twoPi) h -= twoPi;

    const newL = l * data.lightness / 100;  // 调整亮度
    const newC = c * data.chroma / 100;     // 调整色度
    const newA = Math.cos(h) * newC;        // 计算新的a值
    const newB = Math.sin(h) * newC;        // 计算新的b值

    // 6. LAB到XYZ的反向转换
    const fy = (newL + 16) / 116;
    const fx = isNaN(newA) ? fy : fy + newA / 500;
    const fz = isNaN(newB) ? fy : fy - newB / 200;

    const newY = Yn * lab2xyz(fy);
    const newX = Xn * lab2xyz(fx);
    const newZ = Zn * lab2xyz(fz);

    // 7. XYZ到RGB的反向转换
    return [
      xyz2rgb(3.2404542 * newX - 1.5371385 * newY - 0.4985314 * newZ),  // R
      xyz2rgb(-0.969266 * newX + 1.8760108 * newY + 0.041556 * newZ),   // G
      xyz2rgb(0.0556434 * newX - 0.2040259 * newY + 1.0572252 * newZ),  // B
      pixel[3]  // 保持原始alpha通道值
    ];
  } as unknown as Operation
});

// ========== 生命周期钩子 ==========
onMounted(() => {
  if (!mapContainer.value) return;
  
  // 初始化地图
  map = new Map({
    target: mapContainer.value,
    layers: [
      new ImageLayer({
        source: raster // 使用处理后的栅格图层
      })
    ],
    view: new View({
      center: [12950000, 4850000], // 地图中心点
      zoom: 9,
      minZoom: 3,
      maxZoom: 18,
      projection: 'EPSG:3857' // 使用Web墨卡托投影
    })
  });
});

// 组件销毁时清理地图实例
onBeforeUnmount(() => {
  if (map) {
    map.setTarget(undefined); // 解除地图与DOM的绑定
    map = null;
  }
});

// ========== 事件处理 ==========
// 在执行颜色处理前更新数据
raster.on('beforeoperations', (event) => {
  event.data.hue = hue.value;
  event.data.chroma = chroma.value;
  event.data.lightness = lightness.value;
});

// 监听滑块值变化，触发地图重新渲染
watch([hue, chroma, lightness], () => {
  raster.changed();
}, { immediate: true });
</script>

<style scoped>
.gaode-color-map-container {
  width: 100%;
  height: 100%;
  position: relative;
}

.map-view {
  width: 100%;
  height: 100%;
}

/* 控制面板样式 */
.controls {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 1;
  background: rgba(255, 255, 255, 0.85);
  padding: 10px;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.controls td {
  padding: 2px 5px;
}

.controls td:nth-child(3) {
  text-align: right;
  min-width: 4.5em;
}

.controls input[type="range"] {
  width: 200px;
}
</style> 